package com.atguigu.gmall.gateway.filter;

import com.atguigu.gmall.common.utils.IpUtils;
import com.atguigu.gmall.common.utils.JwtUtils;
import com.atguigu.gmall.gateway.config.JwtProperties;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

@EnableConfigurationProperties(JwtProperties.class)
@Component
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.PathConfig> {

    @Autowired
    private JwtProperties properties;

    public AuthGatewayFilterFactory() {
        super(PathConfig.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("paths");
    }

    @Override
    public ShortcutType shortcutType() {
        return ShortcutType.GATHER_LIST;
    }

    @Override
    public GatewayFilter apply(PathConfig config) {
        return (exchange, chain) -> {
            //获取request对象 ServerHttpRequest --> HttpServletRequest
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();

            //1.判断当前请求的路径在不在拦截名单中，不在则直接放行
            List<String> paths = config.paths;//拦截名单
            String curPath = request.getURI().getPath();
            //如果名单不为空，并且当前请求的路径不在拦截名单中，则放行
            if (!CollectionUtils.isEmpty(paths) &&  !paths.stream().anyMatch(path -> curPath.startsWith(path))){
                return chain.filter(exchange);
            }

            //2.获取请求中的token:同步-cookie 异步-头信息
            String token = request.getHeaders().getFirst("token");
            if (StringUtils.isBlank(token)){
                //如果头信息没有获取到，则从cookie中获取
                MultiValueMap<String, HttpCookie> cookies = request.getCookies();
                if (!CollectionUtils.isEmpty(cookies) && cookies.containsKey(this.properties.getCookieName())){
                    HttpCookie cookie = cookies.getFirst(this.properties.getCookieName());
                    token = cookie.getValue();
                }
            }

            //3.判定token是否为空，为空则重定向到登录页面
            if (StringUtils.isBlank(token)){
                response.setStatusCode(HttpStatus.SEE_OTHER);
                response.getHeaders().set(HttpHeaders.LOCATION, "http://sso.gmall.com/toLogin.html?returnUrl=" + request.getURI());
                return response.setComplete();
            }

            try {
                //4.解析token，如果出现异常，则重定向到登录页面
                Map<String, Object> map = JwtUtils.getInfoFromToken(token, this.properties.getPublicKey());

                //5.获取当前请求的ip地址和载荷中的ip地址比较，不一样则重定向到登录界面
                String ip = map.get("ip").toString();//载荷中的ip地址
                String curIp = IpUtils.getIpAddressAtGateway(request);//当前请求中的ip地址
                if (!StringUtils.equals(ip, curIp)){
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                    response.getHeaders().set(HttpHeaders.LOCATION, "http://sso.gmall.com/toLogin.html?returnUrl=" + request.getURI());
                    return response.setComplete();
                }

                //6.把载荷中的用户的登陆信息传递给后续服务
                request.mutate().header("userId", map.get("userId").toString()).build();
                exchange.mutate().request(request).build();

                //7.放行
                return chain.filter(exchange);
            } catch (Exception e) {
                e.printStackTrace();
                //如果出现异常，则重定向到登录页面
                response.setStatusCode(HttpStatus.SEE_OTHER);
                response.getHeaders().set(HttpHeaders.LOCATION, "http://sso.gmall.com/toLogin.html?returnUrl=" + request.getURI());
                return response.setComplete();
            }
        };
    }

    @Data
    public static class PathConfig{
        private List<String> paths;
    }
}
